package reversi;

import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * Obiekt <code>ReversiBoard</code> reprezentuje planszę do gry w Reversi (Othello).
 * Obiekt stanowi coś na kształt silnika(zawiera logikę gry), więc wyposażony jest w niezbędną funkcjonalność,
 * pozwalającą na rozegranie partii.
 * 
 * 
 *
 * @author Sebastian Gabor
 * @version 0.1
 *
 */

public class ReversiBoard {
	

	/**
	 * Konstruktor tworzy planszę i inicjalizuje jej środek. Przyjęto że minimalną możliwą planszą
	 * jest plansza 8 na 8. W zasadzie można tworzyć większe plansze, ale jest to raczej ciekawostka.
	 * 
	 * @param aSizeX - rozmiar planszy w liczbie pól
	 * @param aSizeY - rozmiar planszy w liczbie pól
	 */
	public ReversiBoard(int aSizeX, int aSizeY){
			sizeX=aSizeX;
			sizeY=aSizeY;
	
			if (sizeX<8)
				sizeX=8;
			if (sizeY<8)
				sizeY=8;
			if (sizeX%2!=0)
				sizeX++;			
			if (sizeY%2!=0)
				sizeY++;
			board=new byte[sizeX][sizeY];
			board[sizeX/2][sizeY/2]=RED;
			board[sizeX/2][sizeY/2-1]=GREEN;
			board[sizeX/2-1][sizeY/2-1]=RED;
			board[sizeX/2-1][sizeY/2]=GREEN;
			currentTurn=RED; 
			setMovePossible(true);
			isFull=false;			
	}
	
	/**
	 * Funkcja umieszczająca losowy pionek w losowym punkcie planszy. Raczej zachowana dla ciekawostki
	 * i dla tworzenia rozmaitych wariacji gry.
	 */
	public void randomShot(){
		Random r=new Random();
		board[r.nextInt(sizeX)][r.nextInt(sizeY)]=(byte)(r.nextInt(2)+1);		
	}
	
	/**
	 * Funkcja zmieniająca nazwę aktualnie grającego.
	 */	
	public void endTurn(){
		if (currentTurn==GREEN)
			currentTurn=RED;
		else if (currentTurn==RED)
			currentTurn=GREEN;
		
	}
	
	/**
	 * Funkcja zwracająca stan podanego pola.
	 * 
	 * @param x bezwzględna współrzedna x
	 * @param y bezwzględna współrzedna y
	 * @return stan pola kolor pionka lub puste
	 */
	protected byte getFieldStatus(int x, int y){
		if (x>=0 && x<sizeX &&y>=0 && y<sizeY ){
			return board[x][y];
		}
		else throw new ArrayIndexOutOfBoundsException("Programista był zbyt leniwy, by zrobić to bardziej elegancko");
		
		
	}
	
	/**
	 * Funkcja po zakończonej partii sprawdza kto zwyciężył.
	 * @return Zwraca nazwę zwycięzcy, lub EMPTY dla remisu.
	 */
	public byte findWinner(){
		int red=0;
		int green=0;
		for (int i=0;i<sizeX;i++)
			for (int j=0;j<sizeY;j++)
				if (board[i][j]==RED)
					red++;
				else
					if (board[i][j]==GREEN)
					green++;
				
		if (red>green)
			return RED;
		else if (red<green)
			return GREEN;
		else
			return EMPTY;
		
	}
	
	/**
	 * Funkcja pomocnicza wypisująca możliwe ruchy.
	 * 
	 */

	public void writeAllPossibilities(){
		for (BoardLocation b: nextMove){
			System.out.println(b.getX()+1+" "+(b.getY()+1));
		}
	
	}
	
	/**
	 * Sprawdza część z możliwych zakończeń - plansza zajęta, brak pionów jednego koloru.
	 * 
	 * @return true jeśli koniec  
	 */
	public boolean checkIfFastEnd(){
		boolean f=true;
		boolean iscolor=false;
		for (int i=0; i<sizeX; i++)
			for (int j=0; j<sizeY; j++)
				if (board[i][j]==RED)
					iscolor=true;
		if (!iscolor)
			return true;
		iscolor=false;
		for (int i=0; i<sizeX; i++)
			for (int j=0; j<sizeY; j++)
				if (board[i][j]==GREEN)
					iscolor=true;
		if (!iscolor)
			return true;
		
					
					
			
		for (int i=0; i<sizeX; i++)
			for (int j=0; j<sizeY; j++)
				if (board[i][j]==EMPTY)
					f=false;							
		isFull=f;
		if (isFull)
			return true;
		return false;
				
	}
	/*public boolean checkIfMovePossible(){
		if (!(NextMove.isEmpty()))
			if (CurrentTurn==((BoardLocation)NextMove.element()).getColor())
				return true;
		return false;		
	}*/
	
	
	/**
	 * Na podstawie pola na którym wykonano ostatni ruch przestawia wszystkie możliwe żetony przeciwnika.
	 * 
	 */
	public void swapCoins(BoardLocation lastmove){
		final byte color=lastmove.getColor();
		int x = lastmove.getX();
		int y = lastmove.getY();
		final byte ccolor;
		if (color==GREEN)
			ccolor=RED;
		else
			ccolor=GREEN;
		int k=0;
		int l=0;
		for(k=x+1;k<sizeX;k++){
			if (board[k][y]==ccolor)
				continue;
			else if (board[k][y]==EMPTY)
				break;
			else if (k>x+1 && board[k][y]==color){
				while(k!=x){
					board[k][y]=color;
					k--;					
				}				
			}
			else break;											
		}
		for(k=x-1;k>=0;k--){
			if (board[k][y]==ccolor)
				continue;
			else if (board[k][y]==EMPTY)
				break;
			else if (k<x-1 && board[k][y]==color){
				while(k!=x){
					board[k][y]=color;
					k++;					
				}		
			}
			else break;																
		}
		for(k=y-1;k>=0;k--){
			if (board[x][k]==ccolor)
				continue;
			else if (board[x][k]==EMPTY)
				break;
			else if (k<y-1 && board[x][k]==color){
				while(k!=y){
					board[x][k]=color;
					k++;					
				}					
			}
			else break;																
		}
		for(k=y+1;k<sizeY;k++){
			if (board[x][k]==ccolor)
				continue;
			else if (board[x][k]==EMPTY)
				break;
			else if (k>y+1 && board[x][k]==color){
				while(k!=y){
					board[x][k]=color;
					k--;					
				}			

			}
			else break;																
		}
		k=x+1;
		l=y+1;
		
		while(k<sizeX && l<sizeY){			
			if (board[k][l]==ccolor){
				k++;
				l++;
				continue;
			}
			else if (board[k][l]==EMPTY){
				k++;
				l++;
				break;
			}
			else if (k>x+1 && l>y+1 && board[k][l]==color){				
				while(k!=x && l!=y){
					board[k][l]=color;
					k--;
					l--;									
				}					
			}
			else break;				
		}
		k=x+1;
		l=y-1;
		
		while(k<sizeX && l>=0){		
			if (board[k][l]==ccolor){
				k++;
				l--;
				continue;
			}
			else if (board[k][l]==EMPTY){
				k++;
				l--;
				break;
			}
			else if (k>x+1 && l<y-1 && board[k][l]==color){
				
				while(k!=x && l!=y){
					board[k][l]=color;
					k--;
					l++;									
				}			
				
			}
			else break;									
		}
		k=x-1;
		l=y-1;
		
		while(k>=0 && l>=0){
			
			if (board[k][l]==ccolor){
				k--;
				l--;
				continue;
			}
			else if (board[k][l]==EMPTY){
				k--;
				l--;
				break;
			}
			else if (k<x-1 && l<y-1 && board[k][l]==color){
				
				while(k!=x && l!=y){
					board[k][l]=color;
					k++;
					l++;									
				}			
			}
			else break;									
		}
		k=x-1;
		l=y+1;
		
		while(k>=0 && l<sizeY){
			
			if (board[k][l]==ccolor){
				k--;
				l++;
				continue;
			}
			else if (board[k][l]==EMPTY){
				k--;
				l++;
				break;
			}
			else if (k<x-1 && l>y+1 && board[k][l]==color){
				
				while(k!=x && l!=y){
					board[k][l]=color;
					k++;
					l--;									
				}		
			}
			else break;									
		}	
			
	}
	
	
	public boolean isMovePossible(BoardLocation bl){
		setAllPossibilities(getCurrentTurn());
		for (BoardLocation b: getNextMove()){

			if (b.equalsWithoutStatus(bl))
				return true;
		}
		return false;
	}
	/**
	 * Tworzy listę możliwych ruchów dla danego koloru gracza.
	 * @param aColor - kolor gracza, którego ruchy chcemy zbadać.
	 */
	public void setAllPossibilities(byte aColor){
		nextMove=new ArrayBlockingQueue<BoardLocation>(64);
		setMovePossible(false);
		byte color=aColor;
		byte ccolor;
		if (color==GREEN)
			ccolor=RED;
		else
			ccolor=GREEN;
		
		if (color!=EMPTY){
			//if (color==GREEN){
				for (int i=0;i<sizeX;i++)
					for (int j=0;j<sizeY;j++){
						if (board[i][j]==EMPTY){
							int k,l;
/********************************************************/							
							for(k=i+1;k<sizeX;k++){
								if (board[k][j]==ccolor)
									continue;
								else if (board[k][j]==EMPTY)
									break;
								else if (k>i+1 && board[k][j]==color){
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									break;
								}
								else break;
																
							}
/**********************************************************/							
							for(k=i-1;k>=0;k--){
								if (board[k][j]==ccolor)
									continue;
								else if (board[k][j]==EMPTY)
									break;
								else if (k<i-1 && board[k][j]==color){
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									break;
								}
								else break;																
							}
/************************************************************/							
							for(k=j-1;k>=0;k--){
								if (board[i][k]==ccolor)
									continue;
								else if (board[i][k]==EMPTY)
									break;
								else if (k<j-1 && board[i][k]==color){
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									break;
								}
								else break;																
							}
/************************************************************/							
							//for(k,l=j+1,i+1;k<SizeY;k++){
							k=i+1;
							l=j+1;
							
							while(k<sizeX && l<sizeY){
								
								if (board[k][l]==ccolor){
									k++;
									l++;
									continue;
								}
								else if (board[k][l]==EMPTY){
									k++;
									l++;
									break;
								}
								else if (k>i+1 && l>j+1 && board[k][l]==color){
									
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									k++;
									l++;
									break;
								}
								else break;	
								
							}
/**************************************************/						
							for(k=j+1;k<sizeY;k++){
								if (board[i][k]==ccolor)
									continue;
								else if (board[i][k]==EMPTY)
									break;
								else if (k>j+1 && board[i][k]==color){
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									break;
								}
								else break;																
							}
/****************************************************/
							k=i+1;
							l=j-1;
							
							while(k<sizeX && l>=0){
								
								if (board[k][l]==ccolor){
									k++;
									l--;
									continue;
								}
								else if (board[k][l]==EMPTY){
									k++;
									l--;
									break;
								}
								else if (k>i+1 && l<j-1 && board[k][l]==color){
									
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									k++;
									l--;
									break;
								}
								else break;									
							}
							/****************************************************/
							k=i-1;
							l=j-1;
							
							while(k>=0 && l>=0){
								
								if (board[k][l]==ccolor){
									k--;
									l--;
									continue;
								}
								else if (board[k][l]==EMPTY){
									k--;
									l--;
									break;
								}
								else if (k<i-1 && l<j-1 && board[k][l]==color){
									
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									k--;
									l--;
									break;
								}
								else break;									
							}
							k=i-1;
							l=j+1;
							
							while(k>=0 && l<sizeY){
								
								if (board[k][l]==ccolor){
									k--;
									l++;
									continue;
								}
								else if (board[k][l]==EMPTY){
									k--;
									l++;
									break;
								}
								else if (k<i-1 && l>j+1 && board[k][l]==color){
									
									nextMove.add(new BoardLocation(i,j,color));
									setMovePossible(true);
									k--;
									l++;
									break;
								}
								else break;									
							}	
					}				
			}			
		}	
	}
	
	/**
	 * Ustawia żeton danego koloru na zadanym polu.
	 * @param x - bezwzględna współrzędna pola
	 * @param y - bezwzględna współrzędna pola
	 * @param color 
	 */
	public void setCoin(int x, int y, byte color){
		if ((x>=0 && x<sizeX)&&(y>=0 && x<sizeY))
			board[x][y]=color;		
	}
	public byte getCurrentTurn(){
		return currentTurn;
	}
	public int getSizeX(){
		return sizeX;
		
	}
	public int getSizeY(){
		return sizeY;		
	}

	
	

	/**
	 * Ustawia pole isMovePossible.
	 * @param isMovePossible isMovePossible do ustawienia
	 */
	public void setMovePossible(boolean isMovePossible) {
		this.isMovePossible = isMovePossible;
	}

	/**
	 * Sprawdza czy ruch jest możliwy.
	 * @return the isMovePossible
	 */
	public boolean isMovePossible() {
		return isMovePossible;
	}
	/**
	 * Pobiera kolejkę możliwych ruchów do wykonania.
	 * @return zwraca obiekt Queue<BoardLocation> pełną lub pustą w zależności czy są ruchy do wykonania czy nie.
	 */
	public Queue<BoardLocation> getNextMove(){
		return nextMove;
		
	}
	
	

/*	public int getMovePoints(BoardLocation lastmove){
		final byte color=lastmove.getColor();
		int x = lastmove.getX();
		int y = lastmove.getY();
		final byte ccolor;
		int points=0;
		if (color==GREEN)
			ccolor=RED;
		else
			ccolor=GREEN;
		int k=0;
		int l=0;
		for(k=x+1;k<getSizeX();k++){
			if (getFieldStatus(k, y)==ccolor)
				continue;
			else if (getFieldStatus(k, y)==EMPTY)
				break;
			else if (k>x+1 && getFieldStatus(k, y)==color){
				while(k!=x){					
				//	setCoin(k, y, ccolor);
					k--;
					points++;
				}		
				points--;
				break;
			}
			else break;											
		}
		for(k=x-1;k>=0;k--){
			if (getFieldStatus(k, y)==ccolor)
				continue;
			else if (getFieldStatus(k, y)==EMPTY)
				break;
			else if (k<x-1 && getFieldStatus(k, y)==color){
				while(k!=x){
					points++;
					k++;					
				}	
				points--;
				break;
			}
			else break;																
		}
		for(k=y-1;k>=0;k--){
			if (getFieldStatus(x, k)==ccolor)
				continue;
			else if (getFieldStatus(x, k)==EMPTY)
				break;
			else if (k<y-1 && getFieldStatus(x, k)==color){
				while(k!=y){
					points++;
					k++;					
				}
				points--;
				break;
			}
			else break;																
		}
		for(k=y+1;k<getSizeY();k++){
			if (getFieldStatus(x, k)==ccolor)
				continue;
			else if (getFieldStatus(x, k)==EMPTY)
				break;
			else if (k>y+1 && getFieldStatus(x, k)==color){
				while(k!=y){
					points++;
					k--;					
				}	
				points--;
				break;

			}
			else break;																
		}
		k=x+1;
		l=y+1;
		int s_x=getSizeX();
		int s_y=getSizeY();
		while(k<s_x && l<s_y){			
			if (ccolor==getFieldStatus(k, l)){
				k++;
				l++;
				continue;
			}
			else if (getFieldStatus(k, l)==EMPTY){
				k++;
				l++;
				break;
			}
			else if (k>x+1 && l>y+1 && getFieldStatus(k, l)==color){				
				while(k!=x && l!=y){
					//board[k][l]=color;
					points++;
					k--;
					l--;									
				}	
				points--;
				break;
			}
			else break;				
		}
		k=x+1;
		l=y-1;
		
		while(k<s_x && l>=0){		
			if (getFieldStatus(k, l)==ccolor){
				k++;
				l--;
				continue;
			}
			else if (getFieldStatus(k, l)==EMPTY){
				k++;
				l--;
				break;
			}
			else if (k>x+1 && l<y-1 && getFieldStatus(k, l)==color){
				
				while(k!=x && l!=y){
					points++;
					k--;
					l++;									
				}	
				points--;
				break;
				
			}
			else break;									
		}
		k=x-1;
		l=y-1;
		
		while(k>=0 && l>=0){
			
			if (getFieldStatus(k, l)==ccolor){
				k--;
				l--;
				continue;
			}
			else if (getFieldStatus(k, l)==EMPTY){
				k--;
				l--;
				break;
			}
			else if (k<x-1 && l<y-1 && getFieldStatus(k, l)==color){
				
				while(k!=x && l!=y){
					//board[k][l]=color;
					points++;
					k++;
					l++;									
				}		
				points--;
				break;
			}
			else break;									
		}
		k=x-1;
		l=y+1;
		
		while(k>=0 && l<s_y){
			
			if (getFieldStatus(k, l)==ccolor){
				k--;
				l++;
				continue;
			}
			else if (getFieldStatus(k, l)==EMPTY){
				k--;
				l++;
				break;
			}
			else if (k<x-1 && l>y+1 && getFieldStatus(k, l)==color){
				
				while(k!=x && l!=y){
					points++;
					k++;
					l--;									
				}		
				points--;
				break;
			}
			else break;									
		}	
		return points;
			
	}*/

	/**
	 * Zmienne statyczne zastępujące typ wyliczeniowy Enum.  
	 */
	final static byte EMPTY=0;
	/**
	 * Zmienne statyczne zastępujące typ wyliczeniowy opisujące kolor żetonu.
	 */
	final static byte RED=1;
	/**
	 * Zmienne statyczne zastępujące typ wyliczeniowy opisujące kolor żetonu.
	 */
	final static byte GREEN=2;
	
	/**
	 * Tablica reprezentująca aktualny stan gry.
	 */
	private byte[][] board;
	
	/**
	 * Pole informuje do którego gracza należy aktualny ruch. 
	 */
	private byte currentTurn;
	/**
	 * Pole informuje czy ruch jest możliwy.
	 */
	private boolean isMovePossible;
	/**
	 * Pole informujące czy plansza jest już zapełniona.
	 */
	private boolean isFull;
	/**
	 * Pole informujące o rozmiarze planszy (oś X).
	 */
	private int sizeX;
	/**
	 * Pole informujące o rozmiarze planszy (oś Y).
	 */
	private int sizeY;
	/**
	 * Kolejka możliwych do wykonania ruchów.
	 */
	private Queue<BoardLocation> nextMove;


}
